<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace buwang\service;

use app\common\model\Cache as CacheModel;
use app\common\model\member\Wallet;
use app\common\model\MemberPluginOrder;
use app\common\model\Miniapp;
use app\common\model\MiniappModule;
use app\manage\model\admin\Plugin;
use app\manage\model\AuthGroup;
use app\manage\model\AuthGroupAccess;
use app\manage\model\AuthGroupNode;
use app\manage\model\AuthNode;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use think\Exception;
use think\facade\Cache;
use think\facade\Db;
use buwang\util\File;
use buwang\util\Sql;
use ZipArchive;
use buwang\traits\ErrorTrait;

/**
 * 插件服务类
 * Class PluginService
 * @package buwang\service
 */
class PluginService
{

    use ErrorTrait;

    protected static $name;//插件目录名

    /**
     * 安装插件.
     *
     * @param string $name 插件名称
     * @param bool $force 是否覆盖
     * @param array $extend 扩展参数
     *
     * @return bool
     * @throws Exception
     *
     */
    public static function install($name, $force = false, $extend = [])
    {
        define('ADDONS_DIR', $name);//定义一个常量表示插件目录名
        self::$name = $name;

        try {
            // 检查插件是否完整
            self::check($name);
            if (!$force) {
                //检查有没有冲突的文件
                self::noconflict($name);
            }
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        Db::startTrans();
        try {
            $info = get_addons_info($name);

            //TODO 引入角色组和节点
            self::createMenu($name, $info);

            // 默认启用该插件
            if (!$info['status']) {
                $info['status'] = 1;
                set_addons_info($name, $info);
            }

            // 执行安装脚本
            $class = get_addons_class($name);
            if (class_exists($class)) {
                $addon = new $class(app());
                $addon->install();

                //TODO 设置缓存 若addons/demoPlugin/Plugin.php存在$cache数组
                /*if (isset($addon->cache) && is_array($addon->cache)) {
                    self::installAddonCache($addon->cache, $name);
                }*/
            }
            self::runSQL($name);
            //插件表中增加记录
            Plugin::create($info);

            //把插件目录下的app和public目录复制至对应目录下 addons/demoPlugin/app => app
            //201130 app和public目录移至addons/demoPlugin/install目录下
            foreach (self::getCheckDirs() as $k => $dir) {
                if (is_dir(ADDON_PATH . $name . DS . 'install' . DS . $dir)) {
                    File::copy_dir(ADDON_PATH . $name . DS . 'install' . DS . $dir, app()->getRootPath() . $dir);
                }
            }
            //拷贝静态资源文件 addons/demoPlugin/static/ => public/static/addons/demoPlugin/
            //201130 静态资源文件目录移至addons/demoPlugin/install/static/目录下
            if (file_exists(ADDON_PATH . $name . DS . 'install' . DS . "static" . DS)) {
                File::copy_dir(ADDON_PATH . $name . DS . 'install' . DS . "static" . DS, root_path() . 'public' . DS . 'static' . DS . 'addons' . DS . strtolower($name) . DS);
            }

            Db::commit();
        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }
        // 刷新
        self::refresh();
        return true;
    }

    /**
     * 卸载插件.
     *
     * @param string $name
     * @param bool $force 是否强制卸载
     *
     * @return bool
     * @throws Exception
     *
     */
    public static function uninstall($name, $force = false)
    {
        if (!$name || !is_dir(ADDON_PATH . $name)) {
            throw new Exception('插件不存在');
        }

        define('ADDONS_DIR', $name);//定义一个常量表示插件目录名
        self::$name = $name;

        //查找插件
        $plugin = Plugin::where('name', self::$name)->find();
        if (!$plugin) throw new Exception('插件尚未安装');
        //插件已被购买就无法卸载
        if (MemberPluginOrder::where('plugin_id', $plugin['id'])->find()) throw new Exception('插件已被购买,无法卸载');

        // 执行卸载脚本
        Db::startTrans();
        try {
            // 默认禁用该插件
            $info = get_addons_info($name);

            //删除插件节点 用户组 用户组节点 应用功能的记录
            self::deleteMenu();

            if ($info['status']) {
                $info['status'] = 0;
                set_addons_info($name, $info);
            }
            $class = get_addons_class($name);
            if (class_exists($class)) {
                $addon = new $class(app());
                //清除插件列表缓存
                Cache::has('addonslist') && Cache::delete('addonslist');
                //执行插件应用Plugin.php自定义的卸载方法
                $addon->uninstall();
            }
            self::runSQL($name, 'uninstall');

            //删除插件表中记录
            Plugin::where('name', $name)->delete();

            // 移除插件全局资源文件
            if ($force) {
                $list = self::getGlobalFiles($name);
                foreach ($list as $k => $v) {
                    @unlink(app()->getRootPath() . $v);
                }
            }
            //删除模块前台模板
            if (is_dir(TEMPLATE_PATH . 'addons' . DS . $name . DS)) {
                File::del_dir(TEMPLATE_PATH . 'addons' . DS . $name . DS);
            }
            //静态资源移除
            if (is_dir(root_path() . 'public' . DS . 'static' . DS . 'addons' . DS . strtolower($name) . DS)) {
                File::del_dir(root_path() . 'public' . DS . 'static' . DS . 'addons' . DS . strtolower($name) . DS);
            }

            Db::commit();
        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }
        // 刷新
        self::refresh();
        return true;
    }

    /**
     * 启用.
     *
     * @param string $name 插件名称
     * @param bool $force 是否强制覆盖
     *
     * @return bool
     */
    public static function enable($name, $force = false)
    {
        if (!$name || !is_dir(ADDON_PATH . $name)) {
            throw new Exception('插件不存在！');
        }

        define('ADDONS_DIR', $name);//定义一个常量表示插件目录名
        self::$name = $name;

        $info = get_addons_info($name);

        //执行启用脚本
        try {
            self::enableMenu();

            //更新插件记录表记录状态为1
            Plugin::update(['status' => 1], ['name' => $name]);

            $class = get_addons_class($name);
            if (class_exists($class)) {
                $addon = new $class(app());
                if (method_exists($class, 'enable')) {
                    $addon->enable();
                }
            }

            $info['status'] = 1;
            unset($info['url']);
            set_addons_info($name, $info);
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
        // 刷新
        self::refresh();

        return true;
    }

    /**
     * 禁用.
     *
     * @param string $name 插件名称
     * @param bool $force 是否强制禁用
     *
     * @return bool
     * @throws Exception
     *
     */
    public static function disable($name, $force = false)
    {
        if (!$name || !is_dir(ADDON_PATH . $name)) {
            throw new Exception('插件不存在！');
        }

        define('ADDONS_DIR', $name);//定义一个常量表示插件目录名
        self::$name = $name;

        $info = get_addons_info($name);
        // 执行禁用脚本
        try {
            self::disableMenu();

            //更新插件记录表记录状态为0
            Plugin::update(['status' => 0], ['name' => $name]);

            $class = get_addons_class($name);
            if (class_exists($class)) {
                $addon = new $class(app());

                if (method_exists($class, 'disable')) {
                    $addon->disable();
                }
            }

            $info['status'] = 0;
            unset($info['url']);
            set_addons_info($name, $info);
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        // 刷新
        self::refresh();

        return true;
    }

    /**
     * 租户购买应用
     * @param $user_id
     * @param $plugin
     * @return bool
     * @throws Exception
     */
    public static function buy($user_id, $plugin)
    {
        define('ADDONS_DIR', $plugin['name']);//定义一个常量表示插件目录名
        self::$name = $plugin['name'];

        list($scopes, $type) = explode('_', $plugin['type']);
        if ($scopes !== 'member') throw new Exception('平台插件不可购买');

        //检查是否有group.php
        $path = root_path() . "addons/" . self::$name . "/install/group.php";
        if (!file_exists($path)) return true;
        $data = include $path;
        if (empty($data)) return true;
        $group_name_arr = [];//用户组唯一标识
        foreach ($data as $item) {
            //租户购买时只给租户插入租户角色组
            $item['scopes'] === 'member' && $group_name_arr[] = self::$name . '_' . $item['group_name'];
        }

        //角色id
        $group_ids_arr = AuthGroup::where('group_name', 'IN', $group_name_arr)->where('type', $type === 'system' ? 'system' : 'app')->where('app_name', $type === 'system' ? 'manage' : $type)->column('id');

        //NOTE 账户余额
        //如果价格<=0，就不在查询数据库
        if ($plugin['price'] > 0 && $plugin['price'] > Wallet::getMoney($user_id)) throw new Exception('余额不足');

        try {
            //NOTE 扣余额
            $plugin['price'] > 0 && Wallet::changeMoney($user_id, 'plugin', 0 - $plugin['price'], '购买插件');

            if ($type === 'system') {//系统插件
                //给租户增加角色组
                foreach ($group_ids_arr as $group_id) {
                    $groupAccessParam[] = [
                        'uid' => $user_id,
                        'group_id' => $group_id,
                        'name' => AuthGroup::where('id', $group_id)->value('name'),
                        'scopes' => 'member'
                    ];
                }
                if (!empty($groupAccessParam)) {
                    $authGroupAccess = new AuthGroupAccess();
                    $authGroupAccess->saveAll($groupAccessParam);
                }
            } else {//应用插件 需要调用应用功能购买
                //获取所有功能模块
                $miniapp_module_ids = MiniappModule::where('group_id', 'IN', $group_ids_arr)->column('id');
                foreach ($miniapp_module_ids as $miniapp_module_id) {
                    MiniappService::buy($user_id, $miniapp_module_id, '', false);
                }
            }

            //创建订单
            $pluginOrderParam = [
                'member_id' => $user_id,
                'plugin_id' => $plugin['id'],
                'price' => $plugin['price'],
            ];
            MemberPluginOrder::create($pluginOrderParam);
        } catch (\Throwable $e) {
            throw new Exception($e->getMessage());
        }

        return true;
    }

    /**
     * 刷新插件缓存文件.
     *
     * @return bool
     * @throws Exception
     *
     */
    public static function refresh()
    {
        $file = app()->getRootPath() . 'config' . DS . 'addons.php';

        $config = get_addons_autoload_config(true);

        if ($config['autoload']) {
            return;
        }

        if (!File::is_really_writable($file)) {
            throw new Exception('addons.php文件没有写入权限');
        }

        if ($handle = fopen($file, 'w')) {
            fwrite($handle, "<?php\n\n" . 'return ' . var_export($config, true) . ';');
            fclose($handle);
        } else {
            throw new Exception('文件没有写入权限');
        }

        return true;
    }

    /**
     * 解压插件.
     *
     * @param string $name 插件名称
     *
     * @return string
     * @throws Exception
     *
     */
    public static function unzip($name)
    {
        $file = app()->getRootPath() . 'runtime' . DS . 'addons' . DS . $name . '.zip';
        $dir = ADDON_PATH . $name . DS;
        if (class_exists('ZipArchive')) {
            $zip = new ZipArchive();
            if ($zip->open($file) !== true) {
                throw new Exception('Unable to open the zip file');
            }
            if (!$zip->extractTo($dir)) {
                $zip->close();

                throw new Exception('Unable to extract the file');
            }
            $zip->close();

            return $dir;
        }

        throw new Exception('无法执行解压操作，请确保ZipArchive安装正确');
    }

    /**
     * 注册插件缓存
     * @return boolean
     */
    public static function installAddonCache(array $cache, $name)
    {
        $data = array();
        foreach ($cache as $key => $rs) {
            $add = array(
                'key' => $key,
                'name' => $rs['name'],
                'module' => isset($rs['module']) ? $rs['module'] : $name,
                'model' => $rs['model'],
                'action' => $rs['action'],
                //'param' => isset($rs['param']) ? $rs['param'] : '',
                'system' => 0,
            );
            CacheModel::create($add);
        }
        return true;
    }

    /**
     * 执行数据库脚本
     * @param string $name
     * @param string $Dir
     * @return bool
     * @throws Exception
     */
    public static function runSQL(string $name = '', string $Dir = 'install')
    {
        //数据库安装脚本文件地址 addons/demoPlugin/install/install.sql
        //数据库卸载脚本文件地址 addons/demoPlugin/uninstall/uninstall.sql
        $sql_file = ADDON_PATH . "{$name}" . DS . "{$Dir}" . DS . "{$Dir}.sql";
        if (file_exists($sql_file)) {
            $sql_statement = Sql::getSqlFromFile($sql_file);
            if (!empty($sql_statement)) {
                //TODO 修改表前缀
                $table_prefix = config('database.connections.mysql.prefix');
                if (!empty($table_prefix)) {
                    $sql_statement = str_replace('bw_', $table_prefix, $sql_statement);
                }

                foreach ($sql_statement as $value) {
                    try {
                        Db::execute($value);
                    } catch (\Exception $e) {
                        throw new Exception('导入SQL失败，请检查{$name}应用的sql的语句是否正确');
                    }
                }
            }
        }
        return true;
    }

    /**
     * 是否有冲突
     *
     * @param string $name 插件名称
     * @return  boolean
     * @throws  AddonException
     */
    public static function noconflict($name)
    {
        // 检测冲突文件
        $list = self::getGlobalFiles($name, true);
        if ($list) {
            //发现冲突文件，抛出异常
            throw new Exception("发现冲突文件");
        }
        return true;
    }

    /**
     * 获取插件在全局的文件
     *
     * @param string $name 插件名称
     * @return  array
     */
    public static function getGlobalFiles($name, $onlyconflict = false)
    {
        $list = [];
        $addonDir = ADDON_PATH . $name . DS;
        // 扫描插件目录是否有覆盖的文件
        foreach (self::getCheckDirs() as $k => $dir) {
            $checkDir = root_path() . $dir . DS;
            if (!is_dir($checkDir)) {
                continue;
            }

            //检测到存在插件外目录
            if (is_dir($addonDir . $dir)) {
                //匹配出所有的文件
                $files = new RecursiveIteratorIterator(
                    new RecursiveDirectoryIterator($addonDir . $dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST
                );

                foreach ($files as $fileinfo) {
                    if ($fileinfo->isFile()) {
                        $filePath = $fileinfo->getPathName();
                        $path = str_replace($addonDir, '', $filePath);
                        if ($onlyconflict) {
                            $destPath = app()->getRootPath() . $path;
                            if (is_file($destPath)) {
                                if (filesize($filePath) != filesize($destPath) || md5_file($filePath) != md5_file($destPath)) {
                                    $list[] = $path;
                                }
                            }
                        } else {
                            $list[] = $path;
                        }
                    }
                }
            }
        }
        return $list;
    }

    /**
     * 获取检测的全局文件夹目录
     * @return  array
     */
    protected static function getCheckDirs()
    {
        return [
            'app',
        ];
    }

    /**
     * 检测插件是否完整.
     *
     * @param string $name 插件名称
     *
     * @return bool
     * @throws Exception
     *
     */
    public static function check($name)
    {
        if (!$name || !is_dir(ADDON_PATH . $name)) {
            throw new Exception('插件不存在！');
        }
        $addonClass = get_addons_class($name);
        if (!$addonClass) {
            throw new Exception('插件主启动程序文件Plugin.php不存在');
        }
        $addon = new $addonClass(app());
        if (!$addon->checkInfo()) {
            throw new Exception('配置文件不完整，必须配置项：' . implode(",", ADDON_INFO_TYPE));
        }
        return true;
    }

    /**
     * 导入角色数据
     * @param string $name
     * @param array $info
     * @return bool
     */
    private static function createMenu(string $name, array $info)
    {
        //检查是否有group.php
        $path = root_path() . "/addons/{$name}/install/group.php";
        if (!file_exists($path)) return true;
        $data = include $path;
        if (empty($data)) return true;

        //校验type是否合法
        list($scopes, $type) = explode('_', $info['type']);
        if (!in_array($scopes, ['admin', 'member'])) throw new Exception('插件类型一有误,应为admin或member');
        if ($scopes === 'admin' && $type !== 'system') throw new Exception('平台插件类型二有误,应为system');
        if ($scopes === 'member' && $type !== 'system') {
            //获取应用
            $miniapp = Miniapp::where(['dir' => $type])->find();
            if (!$miniapp) throw new Exception("插件依赖{$type}应用，请先安装{$type}应用");
            //获取应用根节点
            if (!AuthNode::where(["pid" => 0, "app_name" => $type, "menu_path" => $type, "scopes" => $scopes])->find()) throw new Exception("应用{$type}根节点未找到");
        }

        //待插入应用功能
        $modules = [];
        foreach ($data as $item) {
            //参数验证
            if(!isset($item['scopes']) || !in_array($item['scopes'], ['admin', 'member'])) throw new Exception("{$item['name']}角色scopes属性有误");

            //插件角色组可同时包含 平台的角色组和租户的角色组
            $scopes = in_array($item['scopes'], ['admin', 'member']) ? $item['scopes'] : $scopes;

            //角色记录
            $groupParam = [
                //角色名称,展示在角色列表中
                'name' => $item['name'],
                //角色唯一标识,不可重复
                'group_name' => $name . '_' . $item['group_name'],
                //角色备注
                'remark' => isset($item['remark']) ? $item['remark'] : '',
                //登陆类型 admin=总后台,member=租户后台
                'scopes' => $scopes,
                'type' => $type === 'system' ? 'system' : 'app',//角色组类型：system=系统，app=应用，plugin=插件
                'app_name' => $type === 'system' ? 'manage' : $type,//应用名称
            ];
            $group = AuthGroup::create($groupParam);
            if (!$group) throw new Exception("{$item['name']}角色导入失败");

            //应用模块记录
            if ($scopes === 'member' && $type !== 'system') $modules[] = [
                'group_id' => $group['id'],
                'miniapp_id' => $miniapp['id'],
                'name' => $item['name'],
                'type' => 3,//把功能类型修改为插件功能，在应用购买页面禁止直接购买插件功能
                'price' => 0,
                'desc' => $item['remark'] ?: '',
            ];

            //父节点唯一标识
            $parent_node_auth_node = isset($item['parent']) ? $item['parent'] : '';
            //插入节点
            if (isset($item['nodes']) && !empty($item['nodes'])) self::importNodeData($scopes, $type, $group, $item['nodes'], $parent_node_auth_node);
        }

        if ($modules) {
            $miniappModule = new MiniappModule();
            $miniappModule->saveAll($modules);
        }

        return true;
    }

    /**
     * 导入节点
     * @param string $scopes admin/member
     * @param string $type system/bwmall
     * @param array $group 角色组
     * @param array $nodes 节点
     * @param string $parent_node_auth_node 父节点唯一标识
     * @return bool
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private static function importNodeData($scopes, $type, $group, $nodes, $parent_node_auth_node = '')
    {
        if ($parent_node_auth_node) {
            $parent_node = [
                "app_name" => $type === 'system' ? 'manage' : $type,//应用/插件名
                "type" => $type === 'system' ? 'system' : 'app',//权限分类 系统/应用/插件
                "auth_name" => $parent_node_auth_node,//权限标识,必填
                "scopes" => $scopes//scopes
            ];
            $pid = AuthNode::where($parent_node)->value('id');
            if (!$pid) throw new Exception("根节点{$parent_node_auth_node}未找到");
        } else $pid = $scopes === 'admin' ? 188 : ($type === 'system' ? 1314 : AuthNode::where(["pid" => 0, "app_name" => $type, "menu_path" => $type, "scopes" => $scopes])->value('id'));

        //找到该角色组所需的根节点 admin_system是在插件管理/plugin下 member_system是在插件管理member/addons下 member_bwmall是在应用bwmall下
        $rootNodeParam = [
            "pid" => $pid,//上级菜单id,填0代表是顶部选项卡
            "title" => (get_addons_info(self::$name))['title'],//菜单名称
            "app_name" => $type === 'system' ? 'manage' : $type,//应用/插件名
            "type" => $type === 'system' ? 'system' : 'app',//权限分类 系统/应用/插件
            "menu_path" => self::$name,//后台url
            "name" => self::$name,//后台url
            "auth_name" => '',//权限标识,必填
            "param" => '',//参数
            "target" => '_self',//打开方式
            "ismenu" => 1,//是否菜单
            "icon" => '',//图标
            "remark" => '',//备注
            "scopes" => $scopes//scopes
        ];
        $rootNode = AuthNode::where($rootNodeParam)->find();
        if (!$rootNode) $rootNode = AuthNode::create($rootNodeParam);

        //把根节点导入该角色组中
        $groupNodeParam = [
            'group_id' => $group['id'],
            'node_id' => $rootNode['id'],
            'node_name' => $rootNode['name'],
            'auth_name' => $rootNode['auth_name'],
            'type' => $rootNode['type'],
        ];
        AuthGroupNode::create($groupNodeParam);

        self::importNode($scopes, $type, $group, $nodes, $rootNode['id']);

        return true;
    }

    /**
     * 导入节点与角色节点中间表
     * @param array $groups
     * @param array $data
     * @param int $pid 1172是应用管理的节点id
     * @return bool
     * @throws Exception
     */
    private static function importNode($scopes, $type, $group, $nodes, $pid)
    {
        foreach ($nodes as $item) {
            //插入节点
            $nodeParam = [
                "pid" => $pid,//上级菜单id,填0代表是顶部选项卡
                "title" => $item['title'],//菜单名称
                "app_name" => $type === 'system' ? 'manage' : $type,//应用/插件名
                "type" => $type === 'system' ? 'system' : 'app',//权限分类 系统/应用/插件
                "menu_path" => $item['menu_path'],//后台url
                "name" => $item['name'],//后台url
                "auth_name" => isset($item['auth_name']) ? $item['auth_name'] : '',//权限标识,必填
                "param" => isset($item['param']) ? $item['param'] : '',//参数
                "target" => isset($item['target']) ? $item['target'] : '_self',//打开方式
                "ismenu" => isset($item['ismenu']) ? $item['ismenu'] : 0,//是否菜单
                "icon" => isset($item['icon']) ? $item['icon'] : '',//图标
                "remark" => isset($item['remark']) ? $item['remark'] : '',//备注
                "scopes" => $scopes//scopes
            ];
            $node = AuthNode::create($nodeParam);
            if (!$node) throw new Exception("{$item['title']}节点插入失败");

            //插入节点中间表
            $groupNodeParam = [
                'group_id' => $group['id'],
                'node_id' => $node['id'],
                'node_name' => $node['name'],
                'auth_name' => $node['auth_name'],
                'type' => $node['type'],
            ];
            AuthGroupNode::create($groupNodeParam);

            if (isset($item['children']) && !empty($item['children'])) self::importNode($scopes, $type, $group, $item['children'], $node['id']);
        }
        return true;
    }

    /**
     * 删除菜单
     * @return bool
     */
    private static function deleteMenu(): bool
    {
        $info = get_addons_info(self::$name);
        list($scopes, $type) = explode('_', $info['type']);

        $addon_type = $type === 'system' ? 'system' : 'app';
        $addon_app_name = $type === 'system' ? 'manage' : $type;

        //检查是否有group.php
        $path = root_path() . "addons/" . self::$name . "/install/group.php";
        if (!file_exists($path)) return true;
        $data = include $path;
        if (empty($data)) return true;
        $group_name_arr = [];//用户组唯一标识
        foreach ($data as $item) {
            $group_name_arr[] = self::$name . '_' . $item['group_name'];
        }

        //角色id
        $group_ids_arr = AuthGroup::where('group_name', 'IN', $group_name_arr)->where('type', $addon_type)->where('app_name', $addon_app_name)->column('id');
        //节点id
        $node_ids_arr = AuthGroupNode::where('group_id', 'IN', $group_ids_arr)->column('node_id');

        AuthGroup::where('id', 'IN', $group_ids_arr)->delete();
        AuthNode::where('id', 'IN', $node_ids_arr)->delete();
        AuthGroupNode::where('group_id', 'IN', $group_ids_arr)->delete();
        if ($scopes === 'member' && $type !== 'system') MiniappModule::where('group_id', 'IN', $group_ids_arr)->delete();

        return true;
    }

    /**
     * 启动菜单
     */
    private static function enableMenu()
    {
        $info = get_addons_info(self::$name);
        list($scopes, $type) = explode('_', $info['type']);

        //检查是否有group.php
        $path = root_path() . "addons/" . self::$name . "/install/group.php";
        if (!file_exists($path)) return true;
        $data = include $path;
        if (empty($data)) return true;
        $group_name_arr = [];//用户组唯一标识
        foreach ($data as $item) {
            $group_name_arr[] = self::$name . '_' . $item['group_name'];
        }

        //角色组设置为启用
        AuthGroup::where('group_name', 'IN', $group_name_arr)->where('type', $type === 'system' ? 'system' : 'app')->where('app_name', $type === 'system' ? 'manage' : $type)->update(['status' => 1]);

        return true;
    }

    /**
     * 禁用菜单
     */
    private static function disableMenu()
    {
        $info = get_addons_info(self::$name);
        list($scopes, $type) = explode('_', $info['type']);

        //检查是否有group.php
        $path = root_path() . "addons/" . self::$name . "/install/group.php";
        if (!file_exists($path)) return true;
        $data = include $path;
        if (empty($data)) return true;
        $group_name_arr = [];//用户组唯一标识
        foreach ($data as $item) {
            $group_name_arr[] = self::$name . '_' . $item['group_name'];
        }

        //角色组设置为禁用
        AuthGroup::where('group_name', 'IN', $group_name_arr)->where('type', $type === 'system' ? 'system' : 'app')->where('app_name', $type === 'system' ? 'manage' : $type)->update(['status' => 0]);

        return true;
    }
}